﻿using System.Security.Cryptography;

namespace HCMS.Upload.Util
{
    /// <summary>
    /// 上传模块 公共方法
    /// </summary>
    public class UploadCommon
    {
        #region CheckFileTypeByExtName 检查文件类型【根据文件后缀名】
        /// <summary>
        /// 检查文件类型【根据文件后缀名】
        /// </summary>
        /// <remarks>
        /// 这个仅仅作为第一重校验，底层需要通过字节判断文件真实类型
        /// </remarks>
        /// <param name="fileName">文件名称</param>
        /// <param name="fileType">文件类型</param>
        /// <returns></returns>
        public bool CheckFileTypeByExtName(string fileName, UploadFileType fileType)
        {
            var allowExt = new string[] { ".png", ".jpg", ".jpeg", ".gif", ".svg", ".bmp" };
            if (fileType == UploadFileType.Doc)
            {
                allowExt = new string[] { ".txt", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf" };
            }

            var fileExtension = Path.GetExtension(fileName).ToLower();
            return allowExt.Count(t => t == fileExtension) > 0;
        }
        #endregion

        #region CheckFileSize 检查文件是否过大
        /// <summary>
        /// 检查文件是否过大
        /// </summary>
        /// <param name="size">文件大小。单位：bytes</param>
        /// <returns></returns>
        public bool CheckFileSize(int size)
        {
            var num = 10;   // 默认：10M
            var maxSize = num * 1024 * 1024; // 单位：bytes
            return size < maxSize;
        }
        #endregion

        #region IsAllowedExtension 校验上传文件格式是否合法【根据文件字节序列】
        /// <summary>
        /// 校验上传文件格式是否合法
        /// </summary>
        /// <param name="fileBytes">上传文件的字节信息</param>
        /// <param name="fileEx">允许上传的文件格式</param>
        /// <param name="fileex">返回 校验后的文件后缀名</param>
        /// <returns></returns>
        public bool IsAllowedExtension(byte[] fileBytes, FileExtension[] fileEx, out string fileex)
        {
            fileex = "";

            var ms = new MemoryStream(fileBytes);

            #region 方法一
            BinaryReader br = new BinaryReader(ms);
            string fileclass = "";
            byte buffer;
            try
            {
                buffer = br.ReadByte();
                fileclass = buffer.ToString();
                buffer = br.ReadByte();
                fileclass += buffer.ToString();
            }
            catch
            {
            }
            // 注意将文件流指针还原
            ms.Position = 0;
            foreach (FileExtension fe in fileEx)
            {
                if (int.Parse(fileclass) == (int)fe)
                {
                    fileex = fe.ToString();
                    return true;
                }
            }
            #endregion

            #region 方法二、根据文件前几个字节序列，验证上传文件的格式
            if (fileEx.Count(t => t == FileExtension.MP4) > 0)
            {
                byte[] bytes = new byte[500];
                br.Read(bytes, 0, bytes.Length);
                ms.Position = 0;

                // 检测查询的字符是否连续存在
                Func<byte[], byte[], int, int> funcGetIndexOf = delegate (byte[] bAll, byte[] bCheck, int from)
                {
                    #region
                    if (bAll == null || bCheck == null || bAll.Length == 0 || bCheck.Length == 0)
                    {
                        return -1;
                    }

                    int i, j;
                    for (i = from; i < bAll.Length; i++)
                    {
                        if (bAll[i] == bCheck[0])
                        {
                            for (j = 1; j < bCheck.Length; j++)
                            {
                                if (i + j >= bAll.Length)
                                {
                                    break;
                                }

                                if (bAll[i + j] != bCheck[j])
                                {
                                    break;
                                }
                            }
                            if (j == bCheck.Length)
                            {
                                return i;
                            }
                        }
                    }

                    return -1;
                    #endregion
                };

                // 判断是否 MP4
                var isMp4 = funcGetIndexOf(bytes, new byte[] { 0x66, 0x74, 0x79, 0x70 }, 0) >= 0;
                if (isMp4)
                {
                    fileex = "mp4";
                    return true;
                }
            }
            #endregion

            return false;
        }
        #endregion

        #region ImgSaveUrl 图片文件基础路径
        /// <summary>
        /// 图片文件基础路径
        /// </summary>
        /// <returns></returns>
        public string ImgSaveUrl()
        {
            return BaseSaveUrl("upload/img");
        }
        #endregion

        #region DocSaveUrl 文档文件基础路径
        /// <summary>
        /// 文档文件基础路径
        /// </summary>
        /// <returns></returns>
        public string DocSaveUrl()
        {
            return BaseSaveUrl("upload/doc");
        }
        #endregion

        #region FileExtension 文件扩展名的枚举
        /// <summary>
        /// 文件扩展名的枚举 
        /// </summary>
        /// <remarks>
        /// 使用该枚举暂时有缺陷，部分枚举值一样，后面枚举对应值在前面出现过的话会被前一个枚举覆盖，无法得到正确的扩展名信息
        /// 需对上传进行整体优化
        /// </remarks>
        public enum FileExtension
        {
            /// <summary>
            /// 
            /// </summary>
            GIF = 7173,
            /// <summary>
            /// 
            /// </summary>
            JPG = 255216,
            /// <summary>
            /// 
            /// </summary>
            BMP = 6677,
            /// <summary>
            /// 
            /// </summary>
            PNG = 13780,
            /// <summary>
            /// 
            /// </summary>
            DOC = 208207,
            /// <summary>
            /// 
            /// </summary>
            DOCX = 8075,
            /// <summary>
            /// 
            /// </summary>
            XLSX = 8075,
            /// <summary>
            /// 
            /// </summary>
            JS = 239187,
            /// <summary>
            /// 
            /// </summary>
            XLS = 208207,
            /// <summary>
            /// 
            /// </summary>
            SWF = 6787,
            /// <summary>
            /// 
            /// </summary>
            MID = 7784,
            /// <summary>
            /// 
            /// </summary>
            RAR = 8297,
            /// <summary>
            /// 
            /// </summary>
            ZIP = 8075,
            /// <summary>
            /// svg=xml=6063
            /// </summary>
            XML = 6063,
            /// <summary>
            /// 
            /// </summary>
            TXT = 7067,
            /// <summary>
            /// 
            /// </summary>
            MP3 = 7368,
            /// <summary>
            /// 
            /// </summary>
            WMA = 4838,

            /// <summary>
            /// 
            /// </summary>
            PDF = 3780,

            /// <summary>
            /// 
            /// </summary>
            _7Z = 55122,

            /// <summary>
            /// 
            /// </summary>
            PPTX = 8075,

            /// <summary>
            /// 
            /// </summary>
            PPT = 208207,

            /// <summary>
            /// 
            /// </summary>
            WAV = 8273,

            /// <summary>
            /// MP4
            /// </summary>
            /// <remarks>
            /// 这个枚举值，是随便填的，实际不存在。该类型通过字节序列判断格式
            /// </remarks>
            MP4 = 9999999

            // 239187 aspx
            // 117115 cs
            // 119105 js
            // 210187 txt
            // 255254 sql 		
            // 7790 exe dll,
            // 8297 rar
            // 6063 xml
            // 6033 html
        }
        #endregion

        #region BaseSaveUrl 文件保存路径 - 公共方法
        /// <summary>
        /// 文件保存路径 - 公共方法
        /// </summary>
        /// <param name="rootDic">根目录</param>
        /// <returns></returns>
        private string BaseSaveUrl(string rootDic)
        {
            // 根目录
            var d = DateTime.Now;
            var roorUrl = $"/{rootDic}/{d:yyyy}/{d:MM}/{d:dd}/".ToLower();
            return roorUrl;
        }
        #endregion

        #region GetRandomNum 生成指定位数的随机数字(循环中可用)
        /// <summary>
        /// 生成指定位数的随机数字(循环中可用)
        /// </summary>
        /// <param name="num">随机位数(最多只能产生9位随机数)</param>
        /// <returns></returns>
        public int GetRandomNum(int num)
        {
            if (num > 0 && num < 10)
            {
                //在循环中要给随机对象添加随机种子
                Random rnd = new Random(GetRandomSeed());
                //随机数可取该下界值,随机数不能取该上界值
                return rnd.Next((int)Math.Pow(10, num - 1), (int)Math.Pow(10, num));
            }
            return 0;
        }
        /// <summary>
        /// 加密随机数生成器 生成随机种子
        /// </summary>
        /// <returns></returns>
        private int GetRandomSeed()
        {
            byte[] bytes = RandomNumberGenerator.GetBytes(4);
            return BitConverter.ToInt32(bytes, 0);
        }
        #endregion
    }
}
